/**
 * Copyright (c) 2006-2015, JGraph Ltd
 * Copyright (c) 2006-2015, Gaudenz Alder
 */
/**
 * Class: mxWindow
 *
 * Basic window inside a document.
 *
 * Examples:
 *
 * Creating a simple window.
 *
 * (code)
 * var tb = document.createElement('div');
 * var wnd = new mxWindow('Title', tb, 100, 100, 200, 200, true, true);
 * wnd.setVisible(true);
 * (end)
 *
 * Creating a window that contains an iframe.
 *
 * (code)
 * var frame = document.createElement('iframe');
 * frame.setAttribute('width', '192px');
 * frame.setAttribute('height', '172px');
 * frame.setAttribute('src', 'http://www.example.com/');
 * frame.style.backgroundColor = 'white';
 *
 * var w = document.body.clientWidth;
 * var h = (document.body.clientHeight || document.documentElement.clientHeight);
 * var wnd = new mxWindow('Title', frame, (w-200)/2, (h-200)/3, 200, 200);
 * wnd.setVisible(true);
 * (end)
 *
 * To limit the movement of a window, eg. to keep it from being moved beyond
 * the top, left corner the following method can be overridden (recommended):
 *
 * (code)
 * wnd.setLocation = function(x, y)
 * {
 *   x = Math.max(0, x);
 *   y = Math.max(0, y);
 *   mxWindow.prototype.setLocation.apply(this, arguments);
 * };
 * (end)
 *
 * Or the following event handler can be used:
 *
 * (code)
 * wnd.addListener(mxEvent.MOVE, function(e)
 * {
 *   wnd.setLocation(Math.max(0, wnd.getX()), Math.max(0, wnd.getY()));
 * });
 * (end)
 *
 * To keep a window inside the current window:
 *
 * (code)
 * mxEvent.addListener(window, 'resize', mxUtils.bind(this, function()
 * {
 *   var iw = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
 *   var ih = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
 *
 *   var x = this.window.getX();
 *   var y = this.window.getY();
 *
 *   if (x + this.window.table.clientWidth > iw)
 *   {
 *     x = Math.max(0, iw - this.window.table.clientWidth);
 *   }
 *
 *   if (y + this.window.table.clientHeight > ih)
 *   {
 *     y = Math.max(0, ih - this.window.table.clientHeight);
 *   }
 *
 *   if (this.window.getX() != x || this.window.getY() != y)
 *   {
 *     this.window.setLocation(x, y);
 *   }
 * }));
 * (end)
 *
 * Event: mxEvent.MOVE_START
 *
 * Fires before the window is moved. The <code>event</code> property contains
 * the corresponding mouse event.
 *
 * Event: mxEvent.MOVE
 *
 * Fires while the window is being moved. The <code>event</code> property
 * contains the corresponding mouse event.
 *
 * Event: mxEvent.MOVE_END
 *
 * Fires after the window is moved. The <code>event</code> property contains
 * the corresponding mouse event.
 *
 * Event: mxEvent.RESIZE_START
 *
 * Fires before the window is resized. The <code>event</code> property contains
 * the corresponding mouse event.
 *
 * Event: mxEvent.RESIZE
 *
 * Fires while the window is being resized. The <code>event</code> property
 * contains the corresponding mouse event.
 *
 * Event: mxEvent.RESIZE_END
 *
 * Fires after the window is resized. The <code>event</code> property contains
 * the corresponding mouse event.
 *
 * Event: mxEvent.MAXIMIZE
 *
 * Fires after the window is maximized. The <code>event</code> property
 * contains the corresponding mouse event.
 *
 * Event: mxEvent.MINIMIZE
 *
 * Fires after the window is minimized. The <code>event</code> property
 * contains the corresponding mouse event.
 *
 * Event: mxEvent.NORMALIZE
 *
 * Fires after the window is normalized, that is, it returned from
 * maximized or minimized state. The <code>event</code> property contains the
 * corresponding mouse event.
 *
 * Event: mxEvent.ACTIVATE
 *
 * Fires after a window is activated. The <code>previousWindow</code> property
 * contains the previous window. The event sender is the active window.
 *
 * Event: mxEvent.SHOW
 *
 * Fires after the window is shown. This event has no properties.
 *
 * Event: mxEvent.HIDE
 *
 * Fires after the window is hidden. This event has no properties.
 *
 * Event: mxEvent.CLOSE
 *
 * Fires before the window is closed. The <code>event</code> property contains
 * the corresponding mouse event.
 *
 * Event: mxEvent.DESTROY
 *
 * Fires before the window is destroyed. This event has no properties.
 *
 * Constructor: mxWindow
 *
 * Constructs a new window with the given dimension and title to display
 * the specified content. The window elements use the given style as a
 * prefix for the classnames of the respective window elements, namely,
 * the window title and window pane. The respective postfixes are appended
 * to the given stylename as follows:
 *
 *   style - Base style for the window.
 *   style+Title - Style for the window title.
 *   style+Pane - Style for the window pane.
 *
 * The default value for style is mxWindow, resulting in the following
 * classnames for the window elements: mxWindow, mxWindowTitle and
 * mxWindowPane.
 *
 * If replaceNode is given then the window replaces the given DOM node in
 * the document.
 *
 * Parameters:
 *
 * title - String that represents the title of the new window.
 * content - DOM node that is used as the window content.
 * x - X-coordinate of the window location.
 * y - Y-coordinate of the window location.
 * width - Width of the window.
 * height - Optional height of the window. Default is to match the height
 * of the content at the specified width.
 * minimizable - Optional boolean indicating if the window is minimizable.
 * Default is true.
 * movable - Optional boolean indicating if the window is movable. Default
 * is true.
 * replaceNode - Optional DOM node that the window should replace.
 * style - Optional base classname for the window elements. Default is
 * mxWindow.
 */
function mxWindow(
  title,
  content,
  x,
  y,
  width,
  height,
  minimizable,
  movable,
  replaceNode,
  style
) {
  if (content != null) {
    minimizable = minimizable != null ? minimizable : true;
    this.content = content;
    this.init(x, y, width, height, style);

    this.installMaximizeHandler();
    this.installMinimizeHandler();
    this.installCloseHandler();
    this.setMinimizable(minimizable);
    this.setTitle(title);

    if (movable == null || movable) {
      this.installMoveHandler();
    }

    if (replaceNode != null && replaceNode.parentNode != null) {
      replaceNode.parentNode.replaceChild(this.div, replaceNode);
    } else {
      document.body.appendChild(this.div);
    }
  }
}

/**
 * Extends mxEventSource.
 */
mxWindow.prototype = new mxEventSource();
mxWindow.prototype.constructor = mxWindow;

/**
 * Variable: closeImage
 *
 * URL of the image to be used for the close icon in the titlebar.
 */
mxWindow.prototype.closeImage = mxClient.imageBasePath + "/close.gif";

/**
 * Variable: minimizeImage
 *
 * URL of the image to be used for the minimize icon in the titlebar.
 */
mxWindow.prototype.minimizeImage = mxClient.imageBasePath + "/minimize.gif";

/**
 * Variable: normalizeImage
 *
 * URL of the image to be used for the normalize icon in the titlebar.
 */
mxWindow.prototype.normalizeImage = mxClient.imageBasePath + "/normalize.gif";

/**
 * Variable: maximizeImage
 *
 * URL of the image to be used for the maximize icon in the titlebar.
 */
mxWindow.prototype.maximizeImage = mxClient.imageBasePath + "/maximize.gif";

/**
 * Variable: normalizeImage
 *
 * URL of the image to be used for the resize icon.
 */
mxWindow.prototype.resizeImage = mxClient.imageBasePath + "/resize.gif";

/**
 * Variable: visible
 *
 * Boolean flag that represents the visible state of the window.
 */
mxWindow.prototype.visible = false;

/**
 * Variable: minimumSize
 *
 * <mxRectangle> that specifies the minimum width and height of the window.
 * Default is (50, 40).
 */
mxWindow.prototype.minimumSize = new mxRectangle(0, 0, 50, 40);

/**
 * Variable: destroyOnClose
 *
 * Specifies if the window should be destroyed when it is closed. If this
 * is false then the window is hidden using <setVisible>. Default is true.
 */
mxWindow.prototype.destroyOnClose = true;

/**
 * Variable: contentHeightCorrection
 *
 * Defines the correction factor for computing the height of the contentWrapper.
 * Default is 6 for IE 7/8 standards mode and 2 for all other browsers and modes.
 */
mxWindow.prototype.contentHeightCorrection =
  document.documentMode == 8 || document.documentMode == 7 ? 6 : 2;

/**
 * Variable: title
 *
 * Reference to the DOM node (TD) that contains the title.
 */
mxWindow.prototype.title = null;

/**
 * Variable: content
 *
 * Reference to the DOM node that represents the window content.
 */
mxWindow.prototype.content = null;

/**
 * Function: init
 *
 * Initializes the DOM tree that represents the window.
 */
mxWindow.prototype.init = function (x, y, width, height, style) {
  style = style != null ? style : "mxWindow";

  this.div = document.createElement("div");
  this.div.className = style;

  this.div.style.left = x + "px";
  this.div.style.top = y + "px";
  this.table = document.createElement("table");
  this.table.className = style;

  // Disables built-in pan and zoom in IE10 and later
  if (mxClient.IS_POINTER) {
    this.div.style.touchAction = "none";
  }

  // Workaround for table size problems in FF
  if (width != null) {
    if (!mxClient.IS_QUIRKS) {
      this.div.style.width = width + "px";
    }

    this.table.style.width = width + "px";
  }

  if (height != null) {
    if (!mxClient.IS_QUIRKS) {
      this.div.style.height = height + "px";
    }

    this.table.style.height = height + "px";
  }

  // Creates title row
  var tbody = document.createElement("tbody");
  var tr = document.createElement("tr");

  this.title = document.createElement("td");
  this.title.className = style + "Title";

  this.buttons = document.createElement("div");
  this.buttons.style.position = "absolute";
  this.buttons.style.display = "inline-block";
  this.buttons.style.right = "4px";
  this.buttons.style.top = "5px";
  this.title.appendChild(this.buttons);

  tr.appendChild(this.title);
  tbody.appendChild(tr);

  // Creates content row and table cell
  tr = document.createElement("tr");
  this.td = document.createElement("td");
  this.td.className = style + "Pane";

  if (document.documentMode == 7) {
    this.td.style.height = "100%";
  }

  this.contentWrapper = document.createElement("div");
  this.contentWrapper.className = style + "Pane";
  this.contentWrapper.style.width = "100%";
  this.contentWrapper.appendChild(this.content);

  // Workaround for div around div restricts height
  // of inner div if outerdiv has hidden overflow
  if (mxClient.IS_QUIRKS || this.content.nodeName.toUpperCase() != "DIV") {
    this.contentWrapper.style.height = "100%";
  }

  // Puts all content into the DOM
  this.td.appendChild(this.contentWrapper);
  tr.appendChild(this.td);
  tbody.appendChild(tr);
  this.table.appendChild(tbody);
  this.div.appendChild(this.table);

  // Puts the window on top of other windows when clicked
  var activator = mxUtils.bind(this, function (evt) {
    this.activate();
  });

  mxEvent.addGestureListeners(this.title, activator);
  mxEvent.addGestureListeners(this.table, activator);

  this.hide();
};

/**
 * Function: setTitle
 *
 * Sets the window title to the given string. HTML markup inside the title
 * will be escaped.
 */
mxWindow.prototype.setTitle = function (title) {
  // Removes all text content nodes (normally just one)
  var child = this.title.firstChild;

  while (child != null) {
    var next = child.nextSibling;

    if (child.nodeType == mxConstants.NODETYPE_TEXT) {
      child.parentNode.removeChild(child);
    }

    child = next;
  }

  mxUtils.write(this.title, title || "");
  this.title.appendChild(this.buttons);
};

/**
 * Function: setScrollable
 *
 * Sets if the window contents should be scrollable.
 */
mxWindow.prototype.setScrollable = function (scrollable) {
  // Workaround for hang in Presto 2.5.22 (Opera 10.5)
  if (navigator.userAgent.indexOf("Presto/2.5") < 0) {
    if (scrollable) {
      this.contentWrapper.style.overflow = "auto";
    } else {
      this.contentWrapper.style.overflow = "hidden";
    }
  }
};

/**
 * Function: activate
 *
 * Puts the window on top of all other windows.
 */
mxWindow.prototype.activate = function () {
  if (mxWindow.activeWindow != this) {
    var style = mxUtils.getCurrentStyle(this.getElement());
    var index = style != null ? style.zIndex : 3;

    if (mxWindow.activeWindow) {
      var elt = mxWindow.activeWindow.getElement();

      if (elt != null && elt.style != null) {
        elt.style.zIndex = index;
      }
    }

    var previousWindow = mxWindow.activeWindow;
    this.getElement().style.zIndex = parseInt(index) + 1;
    mxWindow.activeWindow = this;

    this.fireEvent(
      new mxEventObject(mxEvent.ACTIVATE, "previousWindow", previousWindow)
    );
  }
};

/**
 * Function: getElement
 *
 * Returuns the outermost DOM node that makes up the window.
 */
mxWindow.prototype.getElement = function () {
  return this.div;
};

/**
 * Function: fit
 *
 * Makes sure the window is inside the client area of the window.
 */
mxWindow.prototype.fit = function () {
  mxUtils.fit(this.div);
};

/**
 * Function: isResizable
 *
 * Returns true if the window is resizable.
 */
mxWindow.prototype.isResizable = function () {
  if (this.resize != null) {
    return this.resize.style.display != "none";
  }

  return false;
};

/**
 * Function: setResizable
 *
 * Sets if the window should be resizable. To avoid interference with some
 * built-in features of IE10 and later, the use of the following code is
 * recommended if there are resizable <mxWindow>s in the page:
 *
 * (code)
 * if (mxClient.IS_POINTER)
 * {
 *   document.body.style.msTouchAction = 'none';
 * }
 * (end)
 */
mxWindow.prototype.setResizable = function (resizable) {
  if (resizable) {
    if (this.resize == null) {
      this.resize = document.createElement("img");
      this.resize.style.position = "absolute";
      this.resize.style.bottom = "2px";
      this.resize.style.right = "2px";

      this.resize.setAttribute("src", this.resizeImage);
      this.resize.style.cursor = "nw-resize";

      var startX = null;
      var startY = null;
      var width = null;
      var height = null;

      var start = mxUtils.bind(this, function (evt) {
        // LATER: pointerdown starting on border of resize does start
        // the drag operation but does not fire consecutive events via
        // one of the listeners below (does pan instead).
        // Workaround: document.body.style.msTouchAction = 'none'
        this.activate();
        startX = mxEvent.getClientX(evt);
        startY = mxEvent.getClientY(evt);
        width = this.div.offsetWidth;
        height = this.div.offsetHeight;

        mxEvent.addGestureListeners(document, null, dragHandler, dropHandler);
        this.fireEvent(new mxEventObject(mxEvent.RESIZE_START, "event", evt));
        mxEvent.consume(evt);
      });

      // Adds a temporary pair of listeners to intercept
      // the gesture event in the document
      var dragHandler = mxUtils.bind(this, function (evt) {
        if (startX != null && startY != null) {
          var dx = mxEvent.getClientX(evt) - startX;
          var dy = mxEvent.getClientY(evt) - startY;

          this.setSize(width + dx, height + dy);

          this.fireEvent(new mxEventObject(mxEvent.RESIZE, "event", evt));
          mxEvent.consume(evt);
        }
      });

      var dropHandler = mxUtils.bind(this, function (evt) {
        if (startX != null && startY != null) {
          startX = null;
          startY = null;
          mxEvent.removeGestureListeners(
            document,
            null,
            dragHandler,
            dropHandler
          );
          this.fireEvent(new mxEventObject(mxEvent.RESIZE_END, "event", evt));
          mxEvent.consume(evt);
        }
      });

      mxEvent.addGestureListeners(this.resize, start, dragHandler, dropHandler);
      this.div.appendChild(this.resize);
    } else {
      this.resize.style.display = "inline";
    }
  } else if (this.resize != null) {
    this.resize.style.display = "none";
  }
};

/**
 * Function: setSize
 *
 * Sets the size of the window.
 */
mxWindow.prototype.setSize = function (width, height) {
  width = Math.max(this.minimumSize.width, width);
  height = Math.max(this.minimumSize.height, height);

  // Workaround for table size problems in FF
  if (!mxClient.IS_QUIRKS) {
    this.div.style.width = width + "px";
    this.div.style.height = height + "px";
  }

  this.table.style.width = width + "px";
  this.table.style.height = height + "px";

  if (!mxClient.IS_QUIRKS) {
    this.contentWrapper.style.height =
      this.div.offsetHeight -
      this.title.offsetHeight -
      this.contentHeightCorrection +
      "px";
  }
};

/**
 * Function: setMinimizable
 *
 * Sets if the window is minimizable.
 */
mxWindow.prototype.setMinimizable = function (minimizable) {
  this.minimize.style.display = minimizable ? "" : "none";
};

/**
 * Function: getMinimumSize
 *
 * Returns an <mxRectangle> that specifies the size for the minimized window.
 * A width or height of 0 means keep the existing width or height. This
 * implementation returns the height of the window title and keeps the width.
 */
mxWindow.prototype.getMinimumSize = function () {
  return new mxRectangle(0, 0, 0, this.title.offsetHeight);
};

/**
 * Function: installMinimizeHandler
 *
 * Installs the event listeners required for minimizing the window.
 */
mxWindow.prototype.installMinimizeHandler = function () {
  this.minimize = document.createElement("img");

  this.minimize.setAttribute("src", this.minimizeImage);
  this.minimize.setAttribute("title", "Minimize");
  this.minimize.style.cursor = "pointer";
  this.minimize.style.marginLeft = "2px";
  this.minimize.style.display = "none";

  this.buttons.appendChild(this.minimize);

  var minimized = false;
  var maxDisplay = null;
  var height = null;

  var funct = mxUtils.bind(this, function (evt) {
    this.activate();

    if (!minimized) {
      minimized = true;

      this.minimize.setAttribute("src", this.normalizeImage);
      this.minimize.setAttribute("title", "Normalize");
      this.contentWrapper.style.display = "none";
      maxDisplay = this.maximize.style.display;

      this.maximize.style.display = "none";
      height = this.table.style.height;

      var minSize = this.getMinimumSize();

      if (minSize.height > 0) {
        if (!mxClient.IS_QUIRKS) {
          this.div.style.height = minSize.height + "px";
        }

        this.table.style.height = minSize.height + "px";
      }

      if (minSize.width > 0) {
        if (!mxClient.IS_QUIRKS) {
          this.div.style.width = minSize.width + "px";
        }

        this.table.style.width = minSize.width + "px";
      }

      if (this.resize != null) {
        this.resize.style.visibility = "hidden";
      }

      this.fireEvent(new mxEventObject(mxEvent.MINIMIZE, "event", evt));
    } else {
      minimized = false;

      this.minimize.setAttribute("src", this.minimizeImage);
      this.minimize.setAttribute("title", "Minimize");
      this.contentWrapper.style.display = ""; // default
      this.maximize.style.display = maxDisplay;

      if (!mxClient.IS_QUIRKS) {
        this.div.style.height = height;
      }

      this.table.style.height = height;

      if (this.resize != null) {
        this.resize.style.visibility = "";
      }

      this.fireEvent(new mxEventObject(mxEvent.NORMALIZE, "event", evt));
    }

    mxEvent.consume(evt);
  });

  mxEvent.addGestureListeners(this.minimize, funct);
};

/**
 * Function: setMaximizable
 *
 * Sets if the window is maximizable.
 */
mxWindow.prototype.setMaximizable = function (maximizable) {
  this.maximize.style.display = maximizable ? "" : "none";
};

/**
 * Function: installMaximizeHandler
 *
 * Installs the event listeners required for maximizing the window.
 */
mxWindow.prototype.installMaximizeHandler = function () {
  this.maximize = document.createElement("img");

  this.maximize.setAttribute("src", this.maximizeImage);
  this.maximize.setAttribute("title", "Maximize");
  this.maximize.style.cursor = "default";
  this.maximize.style.marginLeft = "2px";
  this.maximize.style.cursor = "pointer";
  this.maximize.style.display = "none";

  this.buttons.appendChild(this.maximize);

  var maximized = false;
  var x = null;
  var y = null;
  var height = null;
  var width = null;
  var minDisplay = null;

  var funct = mxUtils.bind(this, function (evt) {
    this.activate();

    if (this.maximize.style.display != "none") {
      if (!maximized) {
        maximized = true;

        this.maximize.setAttribute("src", this.normalizeImage);
        this.maximize.setAttribute("title", "Normalize");
        this.contentWrapper.style.display = "";
        minDisplay = this.minimize.style.display;
        this.minimize.style.display = "none";

        // Saves window state
        x = parseInt(this.div.style.left);
        y = parseInt(this.div.style.top);
        height = this.table.style.height;
        width = this.table.style.width;

        this.div.style.left = "0px";
        this.div.style.top = "0px";
        var docHeight = Math.max(
          document.body.clientHeight || 0,
          document.documentElement.clientHeight || 0
        );

        if (!mxClient.IS_QUIRKS) {
          this.div.style.width = document.body.clientWidth - 2 + "px";
          this.div.style.height = docHeight - 2 + "px";
        }

        this.table.style.width = document.body.clientWidth - 2 + "px";
        this.table.style.height = docHeight - 2 + "px";

        if (this.resize != null) {
          this.resize.style.visibility = "hidden";
        }

        if (!mxClient.IS_QUIRKS) {
          var style = mxUtils.getCurrentStyle(this.contentWrapper);

          if (style.overflow == "auto" || this.resize != null) {
            this.contentWrapper.style.height =
              this.div.offsetHeight -
              this.title.offsetHeight -
              this.contentHeightCorrection +
              "px";
          }
        }

        this.fireEvent(new mxEventObject(mxEvent.MAXIMIZE, "event", evt));
      } else {
        maximized = false;

        this.maximize.setAttribute("src", this.maximizeImage);
        this.maximize.setAttribute("title", "Maximize");
        this.contentWrapper.style.display = "";
        this.minimize.style.display = minDisplay;

        // Restores window state
        this.div.style.left = x + "px";
        this.div.style.top = y + "px";

        if (!mxClient.IS_QUIRKS) {
          this.div.style.height = height;
          this.div.style.width = width;

          var style = mxUtils.getCurrentStyle(this.contentWrapper);

          if (style.overflow == "auto" || this.resize != null) {
            this.contentWrapper.style.height =
              this.div.offsetHeight -
              this.title.offsetHeight -
              this.contentHeightCorrection +
              "px";
          }
        }

        this.table.style.height = height;
        this.table.style.width = width;

        if (this.resize != null) {
          this.resize.style.visibility = "";
        }

        this.fireEvent(new mxEventObject(mxEvent.NORMALIZE, "event", evt));
      }

      mxEvent.consume(evt);
    }
  });

  mxEvent.addGestureListeners(this.maximize, funct);
  mxEvent.addListener(this.title, "dblclick", funct);
};

/**
 * Function: installMoveHandler
 *
 * Installs the event listeners required for moving the window.
 */
mxWindow.prototype.installMoveHandler = function () {
  this.title.style.cursor = "move";

  mxEvent.addGestureListeners(
    this.title,
    mxUtils.bind(this, function (evt) {
      var startX = mxEvent.getClientX(evt);
      var startY = mxEvent.getClientY(evt);
      var x = this.getX();
      var y = this.getY();

      // Adds a temporary pair of listeners to intercept
      // the gesture event in the document
      var dragHandler = mxUtils.bind(this, function (evt) {
        var dx = mxEvent.getClientX(evt) - startX;
        var dy = mxEvent.getClientY(evt) - startY;
        this.setLocation(x + dx, y + dy);
        this.fireEvent(new mxEventObject(mxEvent.MOVE, "event", evt));
        mxEvent.consume(evt);
      });

      var dropHandler = mxUtils.bind(this, function (evt) {
        mxEvent.removeGestureListeners(
          document,
          null,
          dragHandler,
          dropHandler
        );
        this.fireEvent(new mxEventObject(mxEvent.MOVE_END, "event", evt));
        mxEvent.consume(evt);
      });

      mxEvent.addGestureListeners(document, null, dragHandler, dropHandler);
      this.fireEvent(new mxEventObject(mxEvent.MOVE_START, "event", evt));
      mxEvent.consume(evt);
    })
  );

  // Disables built-in pan and zoom in IE10 and later
  if (mxClient.IS_POINTER) {
    this.title.style.touchAction = "none";
  }
};

/**
 * Function: setLocation
 *
 * Sets the upper, left corner of the window.
 */
mxWindow.prototype.setLocation = function (x, y) {
  this.div.style.left = x + "px";
  this.div.style.top = y + "px";
};

/**
 * Function: getX
 *
 * Returns the current position on the x-axis.
 */
mxWindow.prototype.getX = function () {
  return parseInt(this.div.style.left);
};

/**
 * Function: getY
 *
 * Returns the current position on the y-axis.
 */
mxWindow.prototype.getY = function () {
  return parseInt(this.div.style.top);
};

/**
 * Function: installCloseHandler
 *
 * Adds the <closeImage> as a new image node in <closeImg> and installs the
 * <close> event.
 */
mxWindow.prototype.installCloseHandler = function () {
  this.closeImg = document.createElement("img");

  this.closeImg.setAttribute("src", this.closeImage);
  this.closeImg.setAttribute("title", "Close");
  this.closeImg.style.marginLeft = "2px";
  this.closeImg.style.cursor = "pointer";
  this.closeImg.style.display = "none";

  this.buttons.appendChild(this.closeImg);

  mxEvent.addGestureListeners(
    this.closeImg,
    mxUtils.bind(this, function (evt) {
      this.fireEvent(new mxEventObject(mxEvent.CLOSE, "event", evt));

      if (this.destroyOnClose) {
        this.destroy();
      } else {
        this.setVisible(false);
      }

      mxEvent.consume(evt);
    })
  );
};

/**
 * Function: setImage
 *
 * Sets the image associated with the window.
 *
 * Parameters:
 *
 * image - URL of the image to be used.
 */
mxWindow.prototype.setImage = function (image) {
  this.image = document.createElement("img");
  this.image.setAttribute("src", image);
  this.image.setAttribute("align", "left");
  this.image.style.marginRight = "4px";
  this.image.style.marginLeft = "0px";
  this.image.style.marginTop = "-2px";

  this.title.insertBefore(this.image, this.title.firstChild);
};

/**
 * Function: setClosable
 *
 * Sets the image associated with the window.
 *
 * Parameters:
 *
 * closable - Boolean specifying if the window should be closable.
 */
mxWindow.prototype.setClosable = function (closable) {
  this.closeImg.style.display = closable ? "" : "none";
};

/**
 * Function: isVisible
 *
 * Returns true if the window is visible.
 */
mxWindow.prototype.isVisible = function () {
  if (this.div != null) {
    return this.div.style.display != "none";
  }

  return false;
};

/**
 * Function: setVisible
 *
 * Shows or hides the window depending on the given flag.
 *
 * Parameters:
 *
 * visible - Boolean indicating if the window should be made visible.
 */
mxWindow.prototype.setVisible = function (visible) {
  if (this.div != null && this.isVisible() != visible) {
    if (visible) {
      this.show();
    } else {
      this.hide();
    }
  }
};

/**
 * Function: show
 *
 * Shows the window.
 */
mxWindow.prototype.show = function () {
  this.div.style.display = "";
  this.activate();

  var style = mxUtils.getCurrentStyle(this.contentWrapper);

  if (
    !mxClient.IS_QUIRKS &&
    (style.overflow == "auto" || this.resize != null) &&
    this.contentWrapper.style.display != "none"
  ) {
    this.contentWrapper.style.height =
      this.div.offsetHeight -
      this.title.offsetHeight -
      this.contentHeightCorrection +
      "px";
  }

  this.fireEvent(new mxEventObject(mxEvent.SHOW));
};

/**
 * Function: hide
 *
 * Hides the window.
 */
mxWindow.prototype.hide = function () {
  this.div.style.display = "none";
  this.fireEvent(new mxEventObject(mxEvent.HIDE));
};

/**
 * Function: destroy
 *
 * Destroys the window and removes all associated resources. Fires a
 * <destroy> event prior to destroying the window.
 */
mxWindow.prototype.destroy = function () {
  this.fireEvent(new mxEventObject(mxEvent.DESTROY));

  if (this.div != null) {
    mxEvent.release(this.div);
    this.div.parentNode.removeChild(this.div);
    this.div = null;
  }

  this.title = null;
  this.content = null;
  this.contentWrapper = null;
};
